package gdingtalk

import (
	"bytes"
	"errors"
	"gitee.com/dromara/carbon/v2"
	"github.com/gin-gonic/gin"
	"github.com/tidwall/gjson"
	"io/ioutil"
	"log"
	"net/http"
)

type HttpPush struct {
	dingTalkCrypto *DingTalkCrypto
	event          map[string]func(o string) error
	saveLog        func(o string) error
	needSaveLog    bool
}

// defaultSaveLog 默认日志，只保存SYNC_HTTP_PUSH_HIGH和SYNC_HTTP_PUSH_MEDIUM事件
var defaultSaveLog = func(o string) error {
	jsonData := gjson.Parse(o)
	if jsonData.Get("EventType").String() == "SYNC_HTTP_PUSH_HIGH" || jsonData.Get("EventType").String() == "SYNC_HTTP_PUSH_MEDIUM" {
		var opens []OpenSyncBizData
		for _, v := range jsonData.Get("bizData").Array() {
			opens = append(opens, OpenSyncBizData{
				ID:          v.Get("id").Int(),
				GmtCreate:   carbon.CreateFromTimestampMilli(v.Get("gmt_create").Int()).Format("Y-m-d H:i:s"),
				GmtModified: carbon.CreateFromTimestampMilli(v.Get("gmt_modified").Int()).Format("Y-m-d H:i:s"),
				SubscribeId: v.Get("subscribe_id").String(),
				CorpId:      v.Get("corp_id").String(),
				BizId:       v.Get("biz_id").String(),
				BizType:     v.Get("biz_type").Int(),
				BizData:     v.Get("biz_data").String(),
				OpenCursor:  v.Get("open_cursor").Int(),
				Status:      0,
			})
		}
		var tableName string
		if jsonData.Get("EventType").String() == "SYNC_HTTP_PUSH_HIGH" {
			tableName = "open_sync_biz_data"
		} else {
			tableName = "open_sync_biz_data_medium"
		}
		return InsertOpenSyncBizData(opens, tableName)
	}
	return nil
}

// NewHttpPush 初始化
func NewHttpPush() (HttpPush, error) {
	token := gjson.Get(gOption.DingParams, "token").String()
	if token == "" {
		return HttpPush{}, errors.New("token is empty")
	}
	aesKey := gjson.Get(gOption.DingParams, "aesKey").String()
	if aesKey == "" {
		return HttpPush{}, errors.New("aesKey is empty")
	}
	appKey := gjson.Get(gOption.DingParams, "appKey").String()
	if appKey == "" {
		return HttpPush{}, errors.New("appKey is empty")
	}
	httpPush := HttpPush{
		dingTalkCrypto: NewDingTalkCrypto(token, aesKey, appKey),
		event:          make(map[string]func(o string) error),
	}
	// 注册check_url事件
	httpPush.SubscribeEvent("check_url", func(o string) error {
		return nil
	})
	// 以下几个事件是isv模式下才会有的
	// 注册check_create_suite_url事件
	httpPush.SubscribeEvent("check_create_suite_url", func(o string) error {
		return nil
	})
	// 注册check_update_suite_url事件
	httpPush.SubscribeEvent("check_update_suite_url", func(o string) error {
		return nil
	})
	// 注册SYNC_HTTP_PUSH_HIGH事件
	httpPush.SubscribeEvent("SYNC_HTTP_PUSH_HIGH", func(o string) error {
		return nil
	})
	// 注册SYNC_HTTP_PUSH_MEDIUM事件
	httpPush.SubscribeEvent("SYNC_HTTP_PUSH_MEDIUM", func(o string) error {
		return nil
	})
	return httpPush, nil
}

// Handle http处理程序，需要被注册到gin路由中去
func (d *HttpPush) Handle(c *gin.Context) {
	signature := c.Query("signature")
	timestamp := c.Query("timestamp")
	nonce := c.Query("nonce")
	encrypt, _ := ioutil.ReadAll(c.Request.Body)
	c.Request.Body = ioutil.NopCloser(bytes.NewReader(encrypt))
	data, err := d.dingTalkCrypto.GetDecryptMsg(signature, timestamp, nonce, gjson.ParseBytes(encrypt).Get("encrypt").String())
	if err != nil {
		log.Println("ding http push err:", err)
	}
	jsonData := gjson.Parse(data)
	// 保存日志
	if d.needSaveLog {
		go d.saveLog(data)
	}
	if v, ok := d.event[jsonData.Get("EventType").String()]; !ok {
		c.JSON(http.StatusOK, "error")
		return
	} else {
		if v(data) != nil {
			c.JSON(http.StatusOK, "error")
			return
		}
		resData, _ := d.dingTalkCrypto.GetEncryptMsg("success")
		c.JSON(http.StatusOK, resData)
		return
	}
}

// SaveLog 保存到日志
func (d *HttpPush) SaveLog(cb ...func(o string) error) {
	if len(cb) == 0 {
		d.saveLog = defaultSaveLog
	} else {
		d.saveLog = cb[0]
	}
	d.needSaveLog = true
}

// SubscribeEvent 注册事件
func (d *HttpPush) SubscribeEvent(eventType string, cb func(o string) error) {
	d.event[eventType] = cb
}
